﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Pixysoft.TestDrivens;
using Pixysoft.Framework.Configurations.Core;

namespace Pixysoft.Framework.Configurations.Controller
{
    /// <summary>
    /// 思路 
    /// 然后根据list重组为对象
    /// </summary>
    class HtmlController
    {
        private static volatile HtmlController instance;

        private static object syncRoot = new Object();

        public static HtmlController Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        instance = new HtmlController();
                    }
                }

                return instance;

            }
        }


        internal IHtmlDocument Deserialize(string value)
        {
            List<HtmlRawNode> nodes = new HtmlRawNodeReaderState().Read(value);

            HtmlDocument doc = new HtmlDocument();

            if (nodes.Count == 0)
                return doc;

            int index = 0;

            while (index < nodes.Count && index >= 0)
            {
                HtmlRawNode rawnode = nodes[index++];

                if (!rawnode.IsHtml)
                    continue;

                if (rawnode.IsEndElement)
                    continue;

                IHtmlNode htmlnode;

                if (rawnode.IsDeclaration)
                {
                    htmlnode = new HtmlDeclarationNode(rawnode.ElementName, rawnode.Attribute, StringHelper.default_html_pathspliter, 0, doc.Nodes.Count);
                }
                else
                {
                    htmlnode = new HtmlNode(rawnode.ElementName, rawnode.Attribute, StringHelper.default_html_pathspliter, 0, doc.Nodes.Count);

                    Stack<string> stack = new Stack<string>();
                    stack.Push(rawnode.ElementName);
                    index = ConstructNodeBody(stack, nodes, rawnode, htmlnode, index);
                    stack.Pop();
                }

                doc.Nodes.Add(htmlnode);
            }

            return doc;
        }

        private int ConstructNodeBody(Stack<string> parentPath, List<HtmlRawNode> nodes, HtmlRawNode rawparent, IHtmlNode parent, int index)
        {
            while (index < nodes.Count && index >= 0)
            {
                HtmlRawNode rawnode = nodes[index++];

                // 如果当前节点是文字节点 则直接追加到text

                if (!rawnode.IsHtml)
                {
                    //2010-03-31 bug xxx<a>yyy</a>xxx 这种情况text判断会不准确

                    parent.Text += rawnode.Content;

                    continue;
                }


                // 如果当前节点是结束节点 并且不是父节点的结束节点 则退格

                if (rawnode.IsEndElement)
                {
                    //出现非法的结束符，例如<td><img xxx></td>

                    if (!string.Equals(parent.ElementName, rawnode.ElementName, StringComparison.OrdinalIgnoreCase))
                    {
                        if (parentPath.Contains(rawnode.ElementName))
                        {
                            //如果路由路径包含了结束节点的节点 则表示可以倒退

                            index--;
                        }
                        else
                        {
                            //否则跳过

                            continue;
                        }
                    }
                    else
                    {
                        //如果父节点是结束节点 而本节点也是父节点类型的结束节点 则当前节点数据父节点

                        if (rawparent.IsSingleElement)
                        {
                            index--;
                        }
                    }

                    return index;
                }

                //如果父节点不能够拥有当前节点标签 则分离主从关系

                if (!rawparent.CanAppendHtmlBody(rawnode.ElementName))
                {
                    index--;

                    return index;
                }


                IHtmlNode htmlnode;

                if (rawnode.IsDeclaration)
                {
                    htmlnode = new HtmlDeclarationNode(rawnode.ElementName, rawnode.Attribute,
                        parent.ParentPath + parent.NodeIndex + StringHelper.default_html_pathspliter, parent.NestLevel + 1, parent.Nodes.Count);
                }
                else
                {
                    htmlnode = new HtmlNode(rawnode.ElementName, rawnode.Attribute,
                        parent.ParentPath + parent.NodeIndex + StringHelper.default_html_pathspliter, parent.NestLevel + 1, parent.Nodes.Count);

                    if (!rawnode.IsSingleElement)
                    {
                        parentPath.Push(rawnode.ElementName);
                        index = ConstructNodeBody(parentPath, nodes, rawnode, htmlnode, index);
                        parentPath.Pop();
                    }
                    else if (rawnode.CanAppendBody())
                    {
                        //如果当前标签能够有内部html标记，则忽略singleelement 尝试继续读取

                        parentPath.Push(rawnode.ElementName);
                        index = ConstructNodeBody(parentPath, nodes, rawnode, htmlnode, index);
                        parentPath.Pop();
                    }
                }

                parent.Nodes.Add(htmlnode);
            }

            return -1;
        }


        internal IHtmlNode DeserializeFragment(string value)
        {
            List<HtmlRawNode> nodes = new HtmlRawNodeReaderState().Read(value);

            if (nodes.Count == 0)
                return null;

            int index = 0;

            while (index < nodes.Count && index >= 0)
            {
                HtmlRawNode rawnode = nodes[index++];

                if (!rawnode.IsHtml)
                    continue;

                if (rawnode.IsEndElement)
                    continue;

                IHtmlNode htmlnode;

                if (rawnode.IsDeclaration)
                {
                    htmlnode = new HtmlDeclarationNode(rawnode.ElementName, rawnode.Attribute, StringHelper.default_html_pathspliter, 0, 0);
                }
                else
                {
                    htmlnode = new HtmlNode(rawnode.ElementName, rawnode.Attribute, StringHelper.default_html_pathspliter, 0, 0);

                    Stack<string> stack = new Stack<string>();
                    stack.Push(rawnode.ElementName);
                    index = ConstructNodeBody(stack, nodes, rawnode, htmlnode, index);
                    stack.Pop();
                }

                return htmlnode;
            }

            return null;
        }


        internal string TransferToPlainContent(string value)
        {
            List<HtmlRawNode> nodes = new HtmlRawNodeReaderState().Read(value);

            StringBuilder builder = new StringBuilder();

            foreach (HtmlRawNode node in nodes)
            {
                if (node.IsHtml)
                    continue;

                builder.AppendLine(node.Content);
            }

            return builder.ToString();
        }
    }


    /// <summary>
    /// 首先对html标签分割成为list
    /// </summary>
    class HtmlRawNodeReaderState
    {
        /// <summary>
        /// 将Html转化成为List
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public List<HtmlRawNode> Read(string content)
        {
            List<HtmlRawNode> mylist = new List<HtmlRawNode>();

            IEnumerator myiter = content.GetEnumerator();

            StringBuilder mybuilder = new StringBuilder();

            bool isInBlank = false;//是否在括号里面

            bool isInDoubleBlank = false;

            while (myiter.MoveNext())
            {
                string OneChar = myiter.Current.ToString();

                switch (OneChar)
                {
                    case "'": // " ' x ;  ' " Y
                        if (isInBlank && !isInDoubleBlank)
                            isInBlank = false;
                        else if (!isInBlank && !isInDoubleBlank)
                            isInBlank = true;
                        mybuilder.Append(OneChar);
                        break;

                    case "\"":
                        if (isInDoubleBlank && !isInBlank)
                            isInDoubleBlank = false;
                        else if (!isInDoubleBlank && !isInBlank)
                            isInDoubleBlank = true;
                        mybuilder.Append(OneChar);
                        break;

                    case "<":
                        if (isInBlank || isInDoubleBlank)
                        {
                            mybuilder.Append(OneChar);
                            break;
                        }

                        string nodevalue = mybuilder.ToString().Trim();

                        if (!string.IsNullOrEmpty(nodevalue))
                        {
                            mylist.Add(new HtmlRawNode(nodevalue));
                        }

                        if (mybuilder.Length > 0)
                            mybuilder.Remove(0, mybuilder.Length);

                        nodevalue = AppendString(mybuilder, myiter);

                        if (!string.IsNullOrEmpty(nodevalue))
                            mylist.Add(new HtmlRawNode(nodevalue));

                        mybuilder.Remove(0, mybuilder.Length);// clear string builder

                        break;
                    case "\r":
                    //break;
                    case "\n":
                    //break;
                    case " ":
                    //break;
                    default:
                        mybuilder.Append(OneChar);
                        break;
                }

            }
            if (mybuilder.Length > 0)
            {
                string nodevalue = mybuilder.ToString();

                if (!string.IsNullOrEmpty(nodevalue))
                    mylist.Add(new HtmlRawNode(nodevalue));
            }

            return mylist;
        }

        /// <summary>
        /// 寻找结束标签并返回
        /// </summary>
        /// <param name="mybuilder"></param>
        /// <param name="myiter"></param>
        /// <returns></returns>
        private string AppendString(StringBuilder mybuilder, IEnumerator myiter)
        {
            string OneChar = myiter.Current.ToString();
            while (OneChar != ">")
            {
                mybuilder.Append(OneChar);
                myiter.MoveNext();
                OneChar = myiter.Current.ToString();
            }
            mybuilder.Append(OneChar);
            return mybuilder.ToString().Trim();
        }
    }

    /// <summary>
    /// 一个基本的html节点
    /// </summary>
    class HtmlRawNode
    {
        string elementName;//如果是html，获取节点名称
        string attributes;//如果是html，获取节点属性
        string content;//原内容


        bool isHtml = false;//是否html标签
        bool isDeclaration = false;//是否asp.net的声明 例如 %%
        bool isSingleElement = false;// <html />
        bool isEndElement = false;//是否结束html符号 </html>


        public HtmlRawNode(string value)
        {

            content = value;

            if (!value.StartsWith("<"))
            {
                this.isHtml = false;

                return;
            }

            this.isHtml = true;

            ConstructNode(value);

            ConstructIsDeclaration();

            isEndElement = content.StartsWith("</");

            isSingleElement = content.EndsWith("/>");
        }


        /// <summary>
        /// 构造是否声明节点
        /// </summary>
        private void ConstructIsDeclaration()
        {
            if (content.Length < 3)
                return;

            char oneChar = content[1];

            //非字母开头的就是声明节点

            if (oneChar >= 'a' && oneChar <= 'z')
            {
                isDeclaration = false;
            }
            else if (oneChar >= 'A' && oneChar <= 'Z')
            {
                isDeclaration = false;
            }
            else
            {
                isDeclaration = true;
            }

            if (IsDeclaration)
                return;

            //默认的标签是声明节点

            if (string.Equals("link", elementName, StringComparison.OrdinalIgnoreCase))
                IsDeclaration = true;
            else if (string.Equals("meta", elementName, StringComparison.OrdinalIgnoreCase))
                IsDeclaration = true;
        }

        /// <summary>
        /// 负责处理 《 内部的数据 以及递归到内部结构
        /// </summary>
        /// <param name="myiter"></param>
        /// <returns></returns>
        private void ConstructNode(string value)
        {
            CharEnumerator myiter = value.GetEnumerator();

            if (!myiter.MoveNext())
                throw new Exception("read raw node fail.");

            StringBuilder builder = new StringBuilder();

            string rawElementName = null;

            bool isInQuote = false;

            bool isInDoubleQuote = false;

            bool founded = false;

            while (myiter.MoveNext())
            {
                char oneChar = myiter.Current;

                switch (myiter.Current)
                {
                    case ' ':
                        {
                            if (string.IsNullOrEmpty(rawElementName))
                            {
                                //还没有读取头文件 读取头文件 然后继续

                                rawElementName = builder.ToString();

                                //如果是声明的头文件 则跳转到

                                builder = new StringBuilder();
                            }
                            else
                            {
                                //已经读取了头文件 直接添加

                                builder.Append(oneChar.ToString());
                            }

                            break;
                        }
                    case '\'':
                        {
                            if (isInQuote)
                                isInQuote = false;
                            else
                                isInQuote = true;

                            builder.Append(oneChar.ToString());

                            break;
                        }
                    case '\"':
                        {
                            if (isInDoubleQuote)
                                isInDoubleQuote = false;
                            else
                                isInDoubleQuote = true;

                            builder.Append(oneChar.ToString());

                            break;
                        }
                    case '>':
                        {
                            //if (isInQuote || isInDoubleQuote)
                            //{
                            //    //可能是引号内部的数据 忽略

                            //    builder.Append(oneChar.ToString());
                            //}
                            //else
                            //{
                            string builderstring = builder.ToString().Trim();

                            bool endnode = builderstring.EndsWith("/");

                            if (endnode)
                                builderstring = builderstring.Substring(0, builderstring.Length - 1);

                            string rawAttribute = null;

                            if (string.IsNullOrEmpty(rawElementName))
                                rawElementName = builderstring;
                            else
                                rawAttribute = builderstring;

                            if (rawElementName.StartsWith("/"))
                            {
                                rawElementName = rawElementName.Substring(1);
                            }

                            this.attributes = rawAttribute;

                            this.elementName = rawElementName;

                            this.isHtml = true;

                            founded = true;

                            return;

                            //}
                        }
                    default:
                        {
                            builder.Append(oneChar.ToString());

                            break;
                        }
                }
            }
        }

        /// <summary>
        /// 查看是否能追加html内容
        /// </summary>
        /// <returns></returns>
        public bool CanAppendHtmlBody(string bodyelementname)
        {
            if (string.IsNullOrEmpty(bodyelementname))
                return false;

            if (!CanAppendBody())
                return false;

            switch (elementName.Trim().ToUpper())
            {
                case "TD":
                    {
                        if (string.Equals("td", bodyelementname, StringComparison.OrdinalIgnoreCase))
                            return false;

                        if (string.Equals("tr", bodyelementname, StringComparison.OrdinalIgnoreCase))
                            return false;

                        break;
                    }
                case "TR":
                    {
                        if (string.Equals("tr", bodyelementname, StringComparison.OrdinalIgnoreCase))
                            return false;

                        break;
                    }
                default:
                    break;
            }

            return true;
        }

        /// <summary>
        /// 是否能添加节点内容
        /// </summary>
        /// <returns></returns>
        public bool CanAppendBody()
        {
            if (!IsHtml)
                return false;

            if (string.Equals(elementName, "img", StringComparison.OrdinalIgnoreCase))
                return false;

            if (string.Equals(elementName, "input", StringComparison.OrdinalIgnoreCase))
                return false;

            if (string.Equals(elementName, "br", StringComparison.OrdinalIgnoreCase))
                return false;

            if (string.Equals(elementName, "hr", StringComparison.OrdinalIgnoreCase))
                return false;

            if (string.Equals(elementName, "param", StringComparison.OrdinalIgnoreCase))
                return false;

            if (string.Equals(elementName, "embed", StringComparison.OrdinalIgnoreCase))
                return false;

            return true;
        }


        /// <summary>
        /// 节点的原始内容
        /// </summary>
        public string Content
        {
            get { return content; }
            set { content = value; }
        }
        /// <summary>
        /// 是否html节点
        /// </summary>
        public bool IsHtml
        {
            get { return isHtml; }
            set { isHtml = value; }
        }
        /// <summary>
        /// 是否结束节点
        /// </summary>
        public bool IsEndElement
        {
            get { return isEndElement; }
            set { isEndElement = value; }
        }
        /// <summary>
        /// 是否单一节点 没有节点内容
        /// </summary>
        public bool IsSingleElement
        {
            get { return isSingleElement; }
            set { isSingleElement = value; }
        }
        /// <summary>
        /// 是否声明节点
        /// </summary>
        public bool IsDeclaration
        {
            get { return isDeclaration; }
            set { isDeclaration = value; }
        }

        /// <summary>
        /// 节点名字
        /// </summary>
        public string ElementName
        {
            get { return elementName; }
            set { elementName = value; }
        }
        /// <summary>
        /// 节点的属性
        /// </summary>
        public string Attribute
        {
            get { return attributes; }
            set { attributes = value; }
        }

        /// <summary>
        /// 转化为字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return content;
        }
    }
}
